package cn.bluethink.eguan.statistics.service;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;

import cn.bluethink.eguan.core.service.EgFactionService;
import cn.bluethink.eguan.core.service.EgStaffService;
import cn.bluethink.eguan.core.service.EgTavernService;
import cn.bluethink.eguan.model.core.EgOType;
import cn.bluethink.eguan.model.core.EgTaskStatus;
import cn.bluethink.eguan.model.core.EgUser;
import cn.bluethink.eguan.model.okrtask.EgOkrTask;
import cn.bluethink.eguan.model.okrtask.EgTarget;
import cn.bluethink.eguan.model.okrtask.EgTimingType;
import cn.bluethink.eguan.result.PageInfo;
import cn.bluethink.eguan.statistics.entity.EgTargetEntity;
import cn.bluethink.eguan.statistics.entity.OKRTaskEntity;
import cn.bluethink.eguan.statistics.filter.OKRTaskFilter;
import cn.bluethink.eguan.statistics.mapper.EgTargetMapper;
import cn.bluethink.eguan.statistics.mapper.OKRTaskMapper;
import cn.bluethink.eguan.utils.DateUtil;
import cn.bluethink.eguan.utils.OkrTaskUtil;

@Service
public class StatisticService {

	@Autowired
	private OKRTaskMapper taskMapper;
	
	@Autowired
	private EgTargetMapper targetMapper;
	
	@Autowired
	private EgTavernService tavernService;
	
	@Autowired
	private EgStaffService staffService;
	
	@Autowired
	private EgFactionService factionService;
	
	/**
	 * 目标规划与管理
	 * @param filter
	 * @return
	 * @throws Exception
	 */
	public PageInfo<EgOkrTask> taskTarget(OKRTaskFilter filter, EgUser user) throws Exception {
		Integer pageNum = filter.getPageNum() == null ? 1 : filter.getPageNum();
		Integer pageSize = filter.getPageSize() == null ? 20 : filter.getPageSize();
		List<EgOkrTask> result = new ArrayList<>();
		List<OKRTaskEntity> resultentities = new ArrayList<>();

		Long egid = filter.getEgid();
		String whereSql = buildWhereSql(filter, user);
		if(whereSql == null) return new PageInfo<>(new ArrayList<>());
		
		PageHelper.startPage(pageNum, pageSize);
		// 1、从数据库中获取任务列表
		Page<OKRTaskEntity> entities = (Page<OKRTaskEntity>) taskMapper.taskTarget(whereSql);
		if(entities != null && entities.size() > 0) {
			if((egid == null || egid.equals(0))) {
				// 根据驿馆进行分组,使用JAVA8 对List分组,
				//创建LinkedHashMap 容器。这样能保证插入的顺序，和取出的顺序一样
				 LinkedHashMap<Long,List<OKRTaskEntity>> groupBy =  entities.stream().collect(Collectors.groupingBy
					        (OKRTaskEntity::getEgid,LinkedHashMap::new,Collectors.toList()));
				for (Entry<Long, List<OKRTaskEntity>> entry : groupBy.entrySet()) {
					List<OKRTaskEntity> list = entry.getValue();
					resultentities.addAll(list);
				}
				result = resultentities.stream().map(r -> r.createTask()).collect(Collectors.toList());
			}
			result = entities.stream().map(r -> r.createTask()).collect(Collectors.toList());
			return new PageInfo<>(result, entities);
		}
		return new PageInfo<>(result);
	}
	
	/**
	 * 目标跟踪管理查询
	 * @param filter
	 * @return
	 * @throws Exception
	 */
	public PageInfo<EgTarget> tracktarget(OKRTaskFilter filter) throws Exception {
		Integer pageNum = filter.getPageNum() == null ? 1 : filter.getPageNum();
		Integer pageSize = filter.getPageSize() == null ? 100 : filter.getPageSize();
		Long egid = filter.getEgid() == null ? 0 : filter.getEgid();
		Date btime = filter.getBtime();
		Date etime = filter.getEtime();
		Long fid =filter.getFid();
		Integer timing = filter.getTiming();
		List<EgTarget> result = new ArrayList<>();
		if(btime == null || etime == null) {
			// 获取该门派的季度年的开始月份
	        Integer bmonth = factionService.getBmonthByFid(fid);
	        OkrTaskUtil util = new OkrTaskUtil();
	        // 获取当前时间所属的季度年的起止时间
	        List<Date> dates = util.intervalDate(EgTimingType.YEAR_TARGET, new Date(), bmonth);
	        btime = dates.get(0);
	        etime = dates.get(1);
		}
		// 按照门派id,时间及驿馆id查询驿馆下所有目标
		if(fid!=null) {
			PageHelper.startPage(pageNum, pageSize);
			Page<EgTargetEntity> entities = targetMapper.tracktarget(fid,egid, timing, DateUtil.formatDate(btime, "时分秒"), DateUtil.formatDate(etime, "时分秒"));
			if(entities == null || entities.size() == 0) return new PageInfo<>(result);
			// 设置颜色
			relateColor(entities, fid, egid, DateUtil.formatDate(btime, "时分秒"), DateUtil.formatDate(etime, "时分秒"));
			// 设置关键项个数
			setkrNum(entities, btime, etime, egid, fid, timing);
		    // 转换模型
			result = entities.stream().map(r -> r.createTask()).collect(Collectors.toList());
			// 计算标签
			Integer bmonth = factionService.getBmonthByFid(fid);
			result.forEach(e -> {
				e.calculateLabel(bmonth);
			});
			return new PageInfo<>(result, entities);
		}
		
		return new PageInfo<>(result);
	}
	
	/**
	 * 查询任务目标上下级任务目标id
	 * 
	 * @param tid
	 * @return
	 * @throws Exception 
	 */
	public List<Long> relatetargets(Long tid) throws Exception{
		return targetMapper.relatetargetsByTid(tid);
	}
	
	/**
	 * 设置关联的最大目标的颜色
	 * @param entities
	 * @param egid
	 * @throws Exception
	 */
	private void relateColor(List<EgTargetEntity> entities, Long fid, Long egid, String btime, String etime) throws Exception{
		Map<Long, List<Long>> targetChain = targetChain(fid, egid, btime, etime);
		if(targetChain != null && targetChain.size() > 0) {
			// 1、根据条件查询没有关联目标（前置目标）的目标Ids
			List<Long> unrelatedIds = targetMapper.unrelatedIds(fid, egid, btime, etime);
			// 2、遍历没有关联目标的目标Ids 
			for(Long id : unrelatedIds) {
				List<Long> relateIds = targetChain.get(id);
				// 3、如果该目标有子目标
				if(relateIds != null) {
					// 4、获取该目标的颜色
					List<EgTargetEntity> list = entities.stream().filter(e -> e.getTid().equals(id)).collect(Collectors.toList());
					if(list == null || list.size() == 0) break;
					EgTargetEntity entity = entities.stream().filter(e -> e.getTid().equals(id)).collect(Collectors.toList()).get(0);
					Integer color = entity.getColor();
					// 5、遍历关联的所有子目标并设置颜色
					for(Long rid : relateIds) {
						entities.forEach(e -> {
							if(rid.equals(e.getTid())) {
								e.setColor(color);
							}
						});
					}
				}
			}
		}
	 }
	
	/**
	 * 设置目标中的关键项总个数和完成个数
	 * @param entities
	 * @param btime
	 * @param etime
	 * @param egid
	 * @param fid
	 * @param timing
	 * @throws Exception
	 */
	private void setkrNum(List<EgTargetEntity> entities, Date btime, Date etime, Long egid, Long fid, Integer timing) throws Exception{
		
	 
		// 驿馆中所有已完成的关键项
	 	List<Map<String,Object>> completekrs = targetMapper.completekrs(DateUtil.formatDate(btime, "时分秒"),DateUtil.formatDate(etime, "时分秒"), egid,fid,timing);
		// 驿馆中所有关键项
		List<Map<String,Object>> krcount = targetMapper.krcount(DateUtil.formatDate(btime, "时分秒"),DateUtil.formatDate(etime, "时分秒"), egid,fid,timing);
		if(entities!=null && entities.size()>0){
			ArrayList<EgTargetEntity> arrayList = new ArrayList<>();
			for(EgTargetEntity entity : entities) {
				 Integer status = entity.getStatus();
				 //如果任务状态为已完成或者延误完成，目标完成率都是百分百
				 if(status == EgTaskStatus.STATUS_FINISH || status == EgTaskStatus.STATUS_FINISH+1){
					 // 直接传入1即可
					 entity.setCompletekrs(1.0);
					 entity.setKrcount(1.0);
					 arrayList.add(entity);
				  }
				}
			// 移除任务状态为已完成的
			entities.removeAll(arrayList);
			 
			// 循环判断关键项确定个数
			for (EgTargetEntity entity : entities) {
				 // 任务id
				 Long tid = entity.getTid();
				for (Map<String, Object> completekr : completekrs) {
					 if(tid.equals(Long.valueOf(completekr.get("tid").toString()))){
						 // 将关键项完成个数set入实体类
						 entity.setCompletekrs(Double.valueOf(completekr.get("completekrs").toString()));
						   }
						}
				
				for (Map<String, Object> kr : krcount) {
					 if(tid.equals(Long.valueOf(kr.get("tid").toString()))){
						 // 将关键项总个数set入实体类
						 entity.setKrcount(Double.valueOf(kr.get("krcount").toString()));
					}
				 } 
			 }
			// 在还原
			entities.addAll(arrayList);
		 }
      }
 	
	/**
	 * 构建查询语句
	 * @param filter
	 * @return
	 * @throws Exception
	 */
	private String buildWhereSql(OKRTaskFilter filter, EgUser user) throws Exception {
		String whereSql = "";
		
		List<String> whereSqlList = new ArrayList<>();
		
		// 声明过滤条件
		Date btime = filter.getBtime();
		Date etime = filter.getEtime();
		Long fid = filter.getFid();
		Long egid = filter.getEgid();
		Long uid = filter.getUid();
		Integer timing = filter.getTiming() == null ? 0 : filter.getTiming();
		Boolean bTask = filter.getbTask() == null ? false : filter.getbTask();
		Integer status = filter.getStatus();

		whereSqlList.add(" et.otype = " + EgOType.OKR_TASK);
		whereSqlList.add(" et.del_flag = 0 ");

		// 1、查询任务截止日期在btime和etime区间内的列表，包括边界值
		if(btime != null && etime != null) {
			buildStatusSql(whereSqlList, status, btime, etime);
		}
		// 2、查询egid对应的工作任务列表
		if(egid != null && egid > 0){
			whereSqlList.add(" et.pos_oid = " + egid + " AND et.pos_otype = " + EgOType.TAVERN);
		}
		// 3、如果当前用户不为null,查询当前用户所属驿馆列表；否则，是有查看天眼权限的用户，可查看门派下所有驿馆列表
		else if(fid != null && fid > 0){ 
            List<Long> egidList = new ArrayList<>();
			egidList = tavernService.egidsByFid(fid);
			if(egidList == null || egidList.size() == 0) return null;
			if(user != null)  {
				List<Long> userEgList = staffService.taverIdsByUser(user.getId());
				if(userEgList != null && userEgList.size() > 0) {
					egidList.retainAll(userEgList);
				}
			} 
			whereSqlList.add(" et.pos_oid in ( " + StringUtils.collectionToDelimitedString(egidList, ",") + ") AND et.pos_otype = " + EgOType.TAVERN);
			
		}
		
		// 4、查询uid对应的用户创建和被指派的任务列表
		if(uid != null && uid > 0) {
			whereSqlList.add(" (eot.douid = " + uid + " or et.cuid = " + uid + ")");
		}
		// 5、查询任务，即目标标识为空的任务列表
		if(bTask) {
			whereSqlList.add(" eot.timing = 0");
		}
		// 6、否则，查询timing对应的目标（任务）列表
		if(!bTask && timing != null) {
			if(timing > 0) {
				whereSqlList.add(" eot.timing = " + timing);
			} else {
				whereSqlList.add(" eot.timing > 0");
			}
		} 
		// 7、设置查询条件AND连接符
        if (whereSqlList.size() > 0) {
            whereSql += String.join(" AND ", whereSqlList);
        }
		return whereSql;
	}
	
	/**
	 * 根据是否延期 和 日期区间 构建 查询sql语句
	 * @param whereSqlList
	 * @param bDelay
	 * @param firsttime
	 * @param lasttime
	 * @throws Exception
	 */
	private void buildStatusSql(List<String> whereSqlList, Integer status, Date btime, Date etime) throws Exception {
		Timestamp firsttime =  new Timestamp (DateUtil.dateTime(btime, 0, 0, 0).getTime());
		Timestamp lasttime =  new Timestamp (DateUtil.dateTime(etime, 23, 59, 59).getTime());
		if(status == null) {
			// 查询未延期任务列表（即日期区间内的任务列表）
			whereSqlList.add(" et.etime BETWEEN '" + firsttime + "' AND '" + lasttime +"'");
		} else if(status == EgTaskStatus.STATUS_DELAY){
			// 查询已延期的任务列表，即 延误处理中的任务 + 已延误但于日期期间内完成的任务 列表
			whereSqlList.add(" ( ('" + firsttime + "' > et.etime and et.status in  (" + EgTaskStatus.STATUS_DELAY + ", " + (EgTaskStatus.STATUS_HANDLE + 1) + ")) "
					+ "or (et.status = " + (EgTaskStatus.STATUS_FINISH + 1) + " and et.ftime BETWEEN '" + firsttime + "' AND '" + lasttime +"' AND " 
					+ " DATE_FORMAT('" + DateUtil.formatDate(btime, "时分秒") +"', '%Y-%m-%d')" + " > DATE_FORMAT(et.etime, '%Y-%m-%d')))");
		} else if(status == EgTaskStatus.STATUS_PAUSE) {
			whereSqlList.add(" et.status = " + EgTaskStatus.STATUS_PAUSE);
		}
	}
	/**
	 * 获取目标关联关系链
	 * @param entities
	 * @return
	 * @throws Exception
	 */
	private Map<Long, List<Long>> targetChain(Long fid, Long egid, String btime, String etime) throws Exception {
		// 1、根据条件查询没有关联目标（前置目标）的目标Ids
		List<Long> unrelatedIds = targetMapper.unrelatedIds(fid, egid, btime, etime);
		if(unrelatedIds == null || unrelatedIds.size() == 0) return null;
		// 2、根据条件查询前置目标（pre_target)
		List<Long> pretargets = targetMapper.pretargets(fid, egid, btime, etime);
		if(pretargets == null || pretargets.size() == 0) return null;
		// 3、根据条件查询目标Id和其关联的下级目标Ids
		List<EgTargetEntity> relatedmap = targetMapper.relatedtargets(fid, egid, btime, etime);
		if(relatedmap == null || relatedmap.size() == 0) return null;
		Map<Long, List<Long>> relatedtargets = new HashMap<>();
		// 遍历查询结果，并将结果放入relatedtargets
		for(EgTargetEntity entity : relatedmap) {
				List<Long> value = new ArrayList<>();
				if(relatedtargets.containsKey(entity.getParent())) {
					value = relatedtargets.get(entity.getParent());
				} 
				value.addAll(entity.getRelateIds());
				relatedtargets.put(entity.getParent(), value);
			}
		// 4、找出作为前置目标的 目标Ids
		List<Long> baseIds = new ArrayList<>();
		baseIds.addAll(unrelatedIds);
		baseIds.retainAll(pretargets);
		// 5、遍历baseIds，拉出目标关联链条
		Map<Long, List<Long>> chain = new HashMap<>();
		for(Long id : baseIds) {
			// 6、查出该目标关联的下级目标relateds
			List<Long> relateds = relatedtargets.get(id);
			List<Long> result = new ArrayList<>();
			result.addAll(relateds);
			// 7、如果关联的下级目标Ids和前置目标Ids存在交集
			relateds.retainAll(pretargets);
			if(relateds != null && relateds.size() > 0) {
				result = relatedChain(relatedtargets, relateds, pretargets, result);
			}
			chain.put(id, result);
		}
		return chain;	
	}
	
	/**
	 * 获取关联目标Ids
	 * @param relatedtargets
	 * @param relateds
	 * @param pretargets
	 * @param result
	 * @return
	 */
	private List<Long> relatedChain(Map<Long, List<Long>> relatedtargets, List<Long> relateds, List<Long> pretargets, List<Long> result) {
		if(relateds != null && relateds.size() > 0) {
			List<Long> intersection = new ArrayList<>();
			intersection.addAll(relateds);
			intersection.retainAll(result);
			// 1、如果结果中无该关联Id列表，将其放入result中
			if(intersection == null || intersection.size() == 0) result.addAll(relateds);
			// 2、遍历关联的子目标，将子目标中的下级子目标查询出来并set进result中
			for(Long id : relateds) {
				List<Long> correlation = relatedtargets.get(id);
				// 3、如果没有关联关系，跳出循环
				if(correlation == null || correlation.size() == 0) break;
				intersection.clear(); intersection.addAll(correlation);
				intersection.retainAll(pretargets);
				// 4、否则，如果该目标依然为前置目标，遍历查询其子目标
				if(intersection != null && intersection.size() > 0) {
					relatedChain(relatedtargets, correlation, pretargets, result);
				} 
				// 5、否则，直接set进result
				else {
					result.addAll(correlation);
				}
			}
		}
		return result;
	}
}
